using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Chaotic.DataBase.DataAccess
{
	public static class ReflectionExtension
	{
		public static PropertyInfo GetIgnPropertyInfo(this Type type, string name)
		{
			return type.GetProperty(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public);
		}

		public static Type GetPropertyType(this PropertyInfo info)
		{
			Type propertyType = info.PropertyType;
			if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
			{
				return propertyType.GetGenericArguments()[0];
			}
			return propertyType;
		}

		public static void EntityConvert<T1, T2>(T1 a, T2 b) where T1 : class, new() where T2 : class, new()
		{
			Type type = a.GetType();
			PropertyInfo[] properties = type.GetProperties(BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public);
			foreach (PropertyInfo propertyInfo in properties)
			{
				object value = type.GetValue(a, propertyInfo.Name);
				if (value != null)
				{
					b.SetValue(propertyInfo.Name, value);
				}
			}
		}

		public static void SetValue<T, TV>(this T entity, string fieldName, TV fieldValue) where T : class
		{
			if (entity == null)
			{
				return;
			}
			Type typeFromHandle = typeof(T);
			string text = "set_" + fieldName;
			Action<object, object> action;
			if (DataAccessCache.IsCacheExist(typeFromHandle.Name + text))
			{
				action = DataAccessCache.GetCache(typeFromHandle.Name + text) as Action<object, object>;
			}
			else
			{
				MethodInfo method = typeFromHandle.GetMethod(text, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public);
				if (method == null)
				{
					return;
				}
				Type type = method.GetParameters()[0].ParameterType;
				ParameterExpression parameterExpression = Expression.Parameter(typeof(object), "obj");
				ParameterExpression parameterExpression2 = Expression.Parameter(typeof(object), "value");
				UnaryExpression instance = Expression.Convert(parameterExpression, method.ReflectedType);
				UnaryExpression unaryExpression = Expression.Convert(parameterExpression2, type);
				MethodCallExpression body = Expression.Call(instance, method, unaryExpression);
				Expression<Action<object, object>> expression = Expression.Lambda<Action<object, object>>(body, new ParameterExpression[2] { parameterExpression, parameterExpression2 });
				Action<object, object> action2 = expression.Compile();
				action = delegate (object o1, object o2)
				{
					action2.SetTypeValue(o1, type, o2);
				};
				DataAccessCache.SetCache(typeFromHandle.Name + fieldName, action);
			}
			action?.Invoke(entity, fieldValue);
		}

		private static void SetTypeValue(this Action<object, object> setAction, object entity, Type fieldType, object fieldValue)
		{
			try
			{
				if (fieldType.IsEnum)
				{
					int num = fieldValue.ToString().Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries).Aggregate(0, (int current, string filed) => current | (int)Enum.Parse(fieldType, filed));
					setAction(entity, num);
				}
				else if (!(fieldValue.ToString() == string.Empty))
				{
					if (fieldType.IsValueType)
					{
						setAction(entity, fieldType.GetMethod("Parse", new Type[1] { typeof(string) }).Invoke(null, new object[1] { fieldValue.ToString() }));
					}
					else
					{
						setAction(entity, fieldValue);
					}
				}
			}
			catch
			{
				if (fieldType.BaseType != null)
				{
					throw new Exception($"\"{fieldType.BaseType.Name}\"  格式 \"{fieldType.Name}\" 转换失败，请检查。值为:{fieldValue}");
				}
			}
		}

		public static object GetValue<T>(this T entity, string fieldName) where T : class
		{
			if (entity == null)
			{
				return null;
			}
			Type typeFromHandle = typeof(T);
			return typeFromHandle.GetValue(entity, fieldName);
		}

		public static object GetValue(this Type objType, object entity, string fieldName)
		{
			string text = "get_" + fieldName;
			Func<object, object> func;
			if (DataAccessCache.IsCacheExist(objType.Name + text))
			{
				func = DataAccessCache.GetCache(objType.Name + text) as Func<object, object>;
			}
			else
			{
				MethodInfo method = objType.GetMethod(text, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public);
				if (method == null)
				{
					return null;
				}
				ParameterExpression parameterExpression = Expression.Parameter(typeof(object), "obj");
				UnaryExpression instance = Expression.Convert(parameterExpression, method.ReflectedType);
				MethodCallExpression expression = Expression.Call(instance, method);
				UnaryExpression body = Expression.Convert(expression, typeof(object));
				Expression<Func<object, object>> expression2 = Expression.Lambda<Func<object, object>>(body, new ParameterExpression[1] { parameterExpression });
				func = expression2.Compile();
				DataAccessCache.SetCache(objType.Name + fieldName, func);
			}
			return func?.Invoke(entity);
		}

		public static T CreateInstance<T>(this Type type)
		{
			if (DataAccessCache.IsCacheExist(type.FullName))
			{
				if (DataAccessCache.GetCache(type.FullName) is Func<object> func)
				{
					return (T)func();
				}
				return default(T);
			}
			NewExpression body = Expression.New(type);
			Expression<Func<object>> expression = Expression.Lambda<Func<object>>((Expression)body, (ParameterExpression[])null);
			Func<object> func2 = expression.Compile();
			DataAccessCache.SetCache(type.FullName, func2);
			return (T)func2();
		}

		public static T CreateInstance<T>(this Type type, Type[] parameterTypes, object[] objs)
		{
			if (DataAccessCache.IsCacheExist(type.FullName))
			{
				if (DataAccessCache.GetCache(type.FullName) is Func<object[], object> func)
				{
					return (T)func(objs);
				}
			}
			else
			{
				ConstructorInfo constructor = type.GetConstructor(parameterTypes);
				ParameterExpression parameterExpression = Expression.Parameter(typeof(object[]), "_args");
				List<Expression> list = new List<Expression>();
				for (int i = 0; i < parameterTypes.Length; i++)
				{
					BinaryExpression expression = Expression.ArrayIndex(parameterExpression, Expression.Constant(i));
					UnaryExpression item = Expression.Convert(expression, parameterTypes[i]);
					list.Add(item);
				}
				if (constructor != null)
				{
					NewExpression body = Expression.New(constructor, list);
					Expression<Func<object[], object>> expression2 = Expression.Lambda<Func<object[], object>>(body, new ParameterExpression[1] { parameterExpression });
					Func<object[], object> func2 = expression2.Compile();
					DataAccessCache.SetCache(type.FullName, func2);
					return (T)func2(objs);
				}
			}
			return default(T);
		}

		public static T CreateInstance<T>(string assemblyPath, string className, Type[] parameterTypes, object[] objs)
		{
			Assembly assembly = Assembly.LoadFile(assemblyPath);
			return assembly.GetType(className).CreateInstance<T>(parameterTypes, objs);
		}

		public static T CreateInstance<T>(Assembly assembly, string className)
		{
			return assembly.GetType(className).CreateInstance<T>();
		}

		public static T CreateInstance<T>(Assembly assembly, string className, Type[] parameterTypes, object[] objs)
		{
			return assembly.GetType(className).CreateInstance<T>(parameterTypes, objs);
		}

		public static T CreateInstance<T, TV>(Assembly assembly, string className, Type[] parameterTypes, object[] objs)
		{
			Type type = assembly.GetType(className).GetGenericTypeDefinition().MakeGenericType(typeof(TV));
			return type.CreateInstance<T>(parameterTypes, objs);
		}
	}
}